<?php
/**
 * Created by PhpStorm.
 * User: ari
 * Date: 12/3/14
 * Time: 4:37 PM
 */
namespace BC\User;

use BC\SiteIndex;
use BC\User\Grant\Exceptions\MissingGrantContentException;
use BC\User\Service\TestUser;
use BC\User\Session\Exceptions\UserSessionNotFound;
use BC\User\Session\UserSession;
use CPath\Build\IBuildable;
use CPath\Build\IBuildRequest;
use CPath\Render\HTML\Element\Form\HTMLForm;
use CPath\Render\HTML\Element\Form\HTMLInputField;
use CPath\Render\HTML\Element\Form\HTMLSubmit;
use CPath\Render\HTML\Element\HTMLElement;
use CPath\Render\HTML\Header\HTMLHeaderScript;
use CPath\Render\HTML\Header\HTMLHeaderStyleSheet;
use CPath\Render\HTML\Header\HTMLMetaTag;
use CPath\Request\Executable\IExecutable;
use CPath\Request\IRequest;
use CPath\Request\Session\ISessionRequest;
use CPath\Request\Validation\Exceptions\ValidationException;
use CPath\Request\Validation\RequiredValidation;
use CPath\Response\IResponse;
use CPath\Route\IRoutable;
use CPath\Route\RouteBuilder;
use CPath\UnitTest\ITestable;
use CPath\UnitTest\IUnitTestRequest;

class Login implements IRoutable, IExecutable, IBuildable, ITestable {
	const PATH_FORMAT = '/login';
	const PATH_FORMAT2 = '/user/%s/login';

	const FORM_ACTION = 'login';
	const FORM_METHOD = 'POST';

	const FORM_NAME = 'login';

	const PARAM_USER = 'user';
	const PARAM_CHALLENGE_PASSPHRASE = UserSession::PARAM_CHALLENGE_PASSPHRASE;
	const PARAM_PASSPHRASE = UserSession::PARAM_PASSPHRASE;
	const PARAM_CHALLENGE = UserSession::PARAM_CHALLENGE;
	const PARAM_RESUME_SESSION = 'resume-session';
	const PARAM_SESSION_NAME = 'session-name';
	const PARAM_SUBMIT = 'submit';

	/**
	 * Execute a command and return a response. Does not render
	 * @param IRequest $Request
	 * @throws \CPath\Request\Validation\Exceptions\ValidationException
	 * @throws \CPath\Request\Exceptions\RequestException
	 * @return IResponse the execution response
	 */
	function execute(IRequest $Request) {
		$SessionRequest = $Request;
		if(!$SessionRequest instanceof ISessionRequest)
			throw new \InvalidArgumentException("No session present");

		$Form = new HTMLForm(self::FORM_METHOD, self::FORM_ACTION, self::FORM_NAME,
			new HTMLMetaTag(HTMLMetaTag::META_TITLE, 'User Log in'),

			new HTMLHeaderScript(__DIR__ . '\assets\form-login.js'),
			new HTMLHeaderStyleSheet(__DIR__ . '\assets\form-login.css'),

			new HTMLElement('legend', 'content-title', 'Log in'), // as ' . $this->getUser()->getPGPKey()->getUserID()),

			new HTMLElement('fieldset',
				new HTMLElement('legend', 'legend-submit', "Enter user id"),

				new HTMLInputField(self::PARAM_USER,
					new RequiredValidation()
				),
				new HTMLSubmit('Submit')
			)
		);

		if(!isset($Request[self::PARAM_USER]))
			return $Form;

		$userID = $Request[self::PARAM_USER];
		$User = User::searchFirst($Request, $userID);
		$userFingerprint = $User->getFingerprint();

		$Sessions = UserSession::search($userFingerprint);
		if(sizeof($Sessions) === 0)
			throw new UserSessionNotFound("Session not found for user: " . $userFingerprint);
		$UserSession = $Sessions[0];

//		$Request[UserSession::PARAM_USER_FINGERPRINT] = $userFingerprint;
		$Request[UserSession::PARAM_COMMAND] = UserSession::COMMAND_LOGIN;

		return $UserSession->execute($Request);
		//$UserSession::setUserFingerprintFromSession($SessionRequest, $userFingerprint);

	}

	// Static

	static function getRequestPath($userID=null) {
		if($userID) {
			if(strlen($userID) === 40)
				$userID = substr($userID, -8);
			return sprintf(self::PATH_FORMAT2, $userID);
		}
		return sprintf(self::PATH_FORMAT);
	}

	/**
	 * Route the request to this class object and return the object
	 * @param IRequest $Request the IRequest inst for this render
	 * @param array|null $Previous all previous response object that were passed from a handler, if any
	 * @param null|mixed $_arg [varargs] passed by route map
	 * @throws \CPath\Request\Exceptions\RequestException
	 * @return void|bool|Object returns a response object
	 * If nothing is returned (or bool[true]), it is assumed that rendering has occurred and the request ends
	 * If false is returned, this static handler will be called again if another handler returns an object
	 * If an object is returned, it is passed along to the next handler
	 */
	static function routeRequestStatic(IRequest $Request, Array &$Previous = array(), $_arg = null) {
		return new Login();
	}


	/**
	 * Handle this request and render any content
	 * @param IBuildRequest $Request the build request inst for this build session
	 * @return void
	 * @build --disable 0
	 * Note: Use doctag 'build' with '--disable 1' to have this IBuildable class skipped during a build
	 */
	static function handleBuildStatic(IBuildRequest $Request) {
		$RouteBuilder = new RouteBuilder($Request, new SiteIndex());
		$RouteBuilder->writeRoute('ANY ' . self::PATH_FORMAT, __CLASS__); // IRequest::MATCH_NO_SESSION
		//$RouteBuilder->writeRoute('ANY ' . sprintf(self::PATH_FORMAT, ''), __CLASS__);
		$RouteBuilder->writeRoute('ANY ' . sprintf(self::PATH_FORMAT2, ':' . self::PARAM_USER), __CLASS__);
	}

	/**
	 * Perform a unit test
	 * @param IUnitTestRequest $Test the unit test request inst for this test session
	 * @param User $TestUser
	 * @throws ValidationException
	 * @throws \Exception
	 * @return void
	 * @test --disable 0
	 * Note: Use doctag 'test' with '--disable 1' to have this ITestable class skipped during a build
	 */
	static function handleStaticUnitTest(IUnitTestRequest $Test, User $TestUser=null) {
		$TestUser ?: $TestUser = new TestUser($Test);
		$Login = new Login();
		$userFingerprint = $TestUser->getFingerprint();
		$Sessions = UserSession::search($userFingerprint);
		$passphrase = null;
		if(sizeof($Sessions) > 0)
			$UserSession = $Sessions[0];
		else
			$UserSession = UserSession::create($Test, $userFingerprint, 'test-session', $passphrase);

		$passphrase = $TestUser->solveChallengePassphrase($Test, $UserSession);
		$Test->setRequestParameter(Login::PARAM_USER, $TestUser->getFingerprint());
		$Test->setRequestParameter(Login::PARAM_CHALLENGE_PASSPHRASE, $passphrase);
		$Test->setRequestParameter(Login::PARAM_RESUME_SESSION, true);
		$Login->execute($Test);
	}
}